
#include <GL/glu.h>
#include "glDrawFunctions.h"

vector<Point3> colores;

void glDrawModel(Mesh *model, int meshrendermode_mask) {
   
   if (meshrendermode_mask & POINTS) {
      glColor4f(0.0,0.0,0.0,1.0);
      glPushAttrib(GL_LIGHTING_BIT);
      glDisable(GL_LIGHTING);
      glPointSize(1.0);
      glDrawElements(GL_POINTS, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glPopAttrib();
   }
   else if (meshrendermode_mask & WIREFRAME) {
      glColor4f(0.0,0.0,0.0,1.0);
      glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);
      glLineWidth(1.0);
      glDisable(GL_CULL_FACE);
      glDisable(GL_LIGHTING);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glPopAttrib();
   }
   else if (meshrendermode_mask & HIDDENLINES) {
      glColor4f(0.0,0.0,0.0,1.0);
      glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT);
      glLineWidth(1.0);
      glEnable(GL_CULL_FACE);
      glDisable(GL_LIGHTING);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glPopAttrib();
   }
   else if (meshrendermode_mask & FLATLINES) {
      glEnable(GL_POLYGON_OFFSET_FILL);
      glPolygonOffset(1,1);
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glDisable(GL_POLYGON_OFFSET_FILL);
      
      glColor4f(0.1,0.1,0.1,1.0);
      glPushAttrib(GL_ENABLE_BIT | GL_POLYGON_BIT | GL_LINE_BIT | GL_LIGHTING_BIT);
      glLineWidth(1.0);
      glEnable(GL_CULL_FACE);
      glDisable(GL_LIGHTING);
      glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
      
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glPopAttrib();
   }
   else if (meshrendermode_mask & FLAT) {
      glEnable(GL_POLYGON_OFFSET_FILL);
      glPolygonOffset(1,1);
      glShadeModel(GL_FLAT);
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glShadeModel(GL_SMOOTH);
      glDisable(GL_POLYGON_OFFSET_FILL);
   }
   else if (meshrendermode_mask & SMOOTH) {
      glEnable(GL_LIGHTING);
      glEnable(GL_POLYGON_OFFSET_FILL);
      glPolygonOffset(1,1);
      glDrawElements(GL_TRIANGLES, 3*model->nfaces(), GL_UNSIGNED_INT, &model->faces[0]);
      glDisable(GL_POLYGON_OFFSET_FILL);
   }
}

void glDrawModelFaces(Mesh *model) {
   
   unsigned int i=0;
   for (Facet_iterator fi = model->facets_begin(); fi != model->facets_end(); ++fi, ++i) {
      Mesh::Halfedge_around_facet_circulator 
               hbegin = fi->facet_begin(), h = hbegin;
      glPushName(i);
      glBegin(GL_POLYGON);
      do {
         const Point3 &p = h->vertex()->point();
         const Vector3 &n = h->vertex()->normal();
         glNormal3f(n[0], n[1], n[2]);
         glVertex3f(p[0], p[1], p[2]);
      } while (++h != hbegin);
      glEnd();
      glPopName();
   }
}

void glDrawModelFacesFront(Mesh *model, const Vector3 &eye) {
   
   unsigned int i=0;
   for (Facet_iterator fi = model->facets_begin(); fi != model->facets_end(); ++fi, ++i) {
      Mesh::Halfedge_around_facet_circulator 
               hbegin = fi->facet_begin(), h = hbegin;
      if (fi->plane().orthogonal_vector()*eye > 0) continue;
      glPushName(i);
      glBegin(GL_POLYGON);
      do {
         const Point3 &p = h->vertex()->point();
         const Vector3 &n = h->vertex()->normal();
         glNormal3f(n[0], n[1], n[2]);
         glVertex3f(p[0], p[1], p[2]);
      } while (++h != hbegin);
      glEnd();
      glPopName();
   }
}

void glDrawModelVerticesFront(Mesh *model, const Vector3 &eye) {
   
   uint i=0;
   Vertex_iterator vi = model->vertices_begin();
   while (vi != model->vertices_end()) {
      const Vector3 &n = vi->normal();
      //~ if (n*eye < 0) {
         glPushName(i);
         glBegin(GL_POINTS);
         const Point3 &p = vi->point();
         glVertex3f(p[0], p[1], p[2]);
         glEnd();
         glPopName();
      //~ }
      ++vi; ++i;
   }
}

void glDrawSkeleton(USkeleton *skel) {
   
   if (skel==0) return;
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_ENABLE_BIT | GL_POINT_BIT | GL_LINE_BIT);
   glDisable(GL_LIGHTING);
   glDisable(GL_DEPTH_TEST);
   glEnable(GL_POINT_SMOOTH);
   glEnable(GL_LINE_SMOOTH);
   glEnable(GL_BLEND);
   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
   glLineWidth(1.0);
   glPointSize(10.0);
   glColor3d(0.0, 0.0, 0.5);
   for (int i=0; i<skel->nJoints(); ++i) {
      const Point3 &p = skel->joint(i);
      glPushName(i);
      glBegin(GL_POINTS);
      glVertex3d(p[0], p[1], p[2]);
      glEnd();
      glPopName();
   }
   for (int i=0; i<skel->nBones(); ++i) {
      const Segment3 &s = skel->boneSegment(i);
      glBegin(GL_LINES);
      glVertex3d(s[0][0], s[0][1], s[0][2]);
      glVertex3d(s[1][0], s[1][1], s[1][2]);
      glEnd();
   }
   glPopAttrib();
}

void glDrawSkeletonJoints(USkeleton *skel) {
   
   if (skel==0) return;
   glPointSize(10.0);
   for (int i=0; i<skel->nJoints(); ++i) {
      const Point3 &p = skel->joint(i);
      glPushName(i);
      glBegin(GL_POINTS);
      glVertex3d(p[0], p[1], p[2]);
      glEnd();
      glPopName();
   }
   glPointSize(1.0);
}

void glDrawSkeletonBones(USkeleton *skel) {
   
   if (skel==0) return;
   for (int i=0; i<skel->nBones(); ++i) {
      const Segment3 &s = skel->boneSegment(i);
      glPushName(i);
      glBegin(GL_LINES);
      glVertex3d(s[0][0], s[0][1], s[0][2]);
      glVertex3d(s[1][0], s[1][1], s[1][2]);
      glEnd();
      glPopName();
   }
}

/// Draws a 3D cursor
void drawCursor3D(const Point3 &c, Number sf) {
   
   glDisable(GL_LIGHTING);
   glColor4f(1.0,0.5,0.0,1.0);
   glLineWidth(1.0);
   
   glBegin(GL_LINE_STRIP);
   Vector3 V(-1,0,0); Vector3 uV(0,1,0); /// V = crossproduct(Vector3(0,0,1),uV)
   for (float t=0; t<2.0*3.2; t+=0.1) {
      Point3 p = c + sf*cos(t)*uV + sf*sin(t)*V;
      glVertex3f(p[0], p[1], p[2]);
   }
   glEnd();
   
   Point3 p0 = c + 0.5*sf*Vector3(1,0,0);
   Point3 p1 = c + 1.5*sf*Vector3(1,0,0);
   Point3 p2 = c + 0.5*sf*Vector3(0,1,0);
   Point3 p3 = c + 1.5*sf*Vector3(0,1,0);
   Point3 p4 = c + 0.5*sf*Vector3(-1,0,0);
   Point3 p5 = c + 1.5*sf*Vector3(-1,0,0);
   Point3 p6 = c + 0.5*sf*Vector3(0,-1,0);
   Point3 p7 = c + 1.5*sf*Vector3(0,-1,0);
   
   glColor4f(1.0,0.0,0.0,1.0);
   glBegin(GL_LINES);
   glVertex3f(p0[0], p0[1], p0[2]);
   glVertex3f(p1[0], p1[1], p1[2]);
   glVertex3f(p2[0], p2[1], p2[2]);
   glVertex3f(p3[0], p3[1], p3[2]);
   glVertex3f(p4[0], p4[1], p4[2]);
   glVertex3f(p5[0], p5[1], p5[2]);
   glVertex3f(p6[0], p6[1], p6[2]);
   glVertex3f(p7[0], p7[1], p7[2]);
   glEnd();
   
   glEnable(GL_LIGHTING);
}

void glDrawVertexSkinning(Mesh *model, USkeleton *skeleton, int idvertex, bool usingbones) {
   
   if (idvertex < 0) return;
   
   int c=0;
   glDisable(GL_LIGHTING);
   Vertex_iterator vi = model->vertices_begin();
   advance(vi, idvertex);
   const Point3 &p = vi->point();
   for (int i=0; i<vi->weights.size(); ++i) {
      if (vi->weights[i] == 0) continue;
      ++c;
      glColor4f(1.0,0.0,0.0,1.0);
      glBegin(GL_LINES);
      if (usingbones) {
         const Segment3 &s = skeleton->boneSegment(i);
         glVertex3f(p[0], p[1], p[2]);
         glVertex3f((s[0][0]+s[1][0])*0.5, (s[0][1]+s[1][1])*0.5, (s[0][2]+s[1][2])*0.5);
      }
      else {
         const Point3 &jo = skeleton->getJointPos(i);
         glVertex3f(p[0], p[1], p[2]);
         glVertex3f(jo[0], jo[1], jo[2]);
      }
      glEnd();
      cout<<vi->weights[i]<<" ";
   }
   cout<<endl<<"   number of neighbors: "<<c<<endl;
   glEnable(GL_LIGHTING);
}

///
/// below drawing function for rigging/skinning
void glDrawRiggingSkinning(Mesh *model, USkeleton *skeleton, int mode_mask, bool usingbones) {
   
   if (model == 0) return;
   if (skeleton == 0) return;
   
   if (mode_mask & RIGGING) {
      glDrawRigging(model);
   }
   else if (mode_mask & SKINNING) {
      //glDrawSkinning(model);
   }
   else if (mode_mask & RIGGINGEDGES) {
      glDrawRiggingEdges(model, skeleton, usingbones);
   }
}

void glDrawRigging(Mesh *model) {
   
   if (model == 0) return;
   for (Facet_iterator fi = model->facets_begin(); fi != model->facets_end(); ++fi) {
      Halfedge_handle hh = fi->halfedge(), hhbegin = hh;
      glBegin(GL_POLYGON);
      do {
         assert(hh->vertex()->rigging_group != -1);
         int idx = hh->vertex()->rigging_group%colores.size();
         GLfloat color[4] = {colores[idx][0], colores[idx][1], colores[idx][2], 1.0f};
         glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
         const Point3 &p = hh->vertex()->point();
         const Vector3 &n = hh->vertex()->normal();
         glColor4fv(color);
         glNormal3f(n[0], n[1], n[2]);
         glVertex3f(p[0], p[1], p[2]);
         hh = hh->next(); //
      } while(hh != hhbegin);
      glEnd();
   }
}

void glDrawSkinning(Mesh *model) {
   
   if (model == 0) return;
   GLfloat color[4];
   for (Facet_iterator fi = model->facets_begin(); fi != model->facets_end(); ++fi) {
      Halfedge_handle hh = fi->halfedge(), hhbegin = hh;
      glBegin(GL_POLYGON);
      do {
         unsigned int nminusone = std::count(hh->vertex()->weights.begin(), 
                                       hh->vertex()->weights.end(), 1.0);
         if (nminusone != 1) {
            vector<Number> ltmp(hh->vertex()->weights);
            std::sort(ltmp.begin(), ltmp.end());
            Number large = ltmp.back();
            if (large > 0.9) {
               int idx = hh->vertex()->rigging_group%colores.size();
               color[0] = colores[idx][0];
               color[1] = colores[idx][1];
               color[2] = colores[idx][2];
               color[3] = 1.0f;
            }
            else {
               color[0] = large;
               color[1] = large;
               color[2] = large;
               color[3] = 1.0f;
            }
         }
         else {
            int idx = hh->vertex()->rigging_group%colores.size();
            color[0] = colores[idx][0];
            color[1] = colores[idx][1];
            color[2] = colores[idx][2];
            color[3] = 1.0f;
         }
         glMaterialfv(GL_FRONT, GL_DIFFUSE, color);
         const Point3 &p = hh->vertex()->point();
         const Vector3 &n = hh->vertex()->normal();
         glColor4fv(color);
         glNormal3f(n[0], n[1], n[2]);
         glVertex3f(p[0], p[1], p[2]);
         hh = hh->next();
      } while(hh != hhbegin);
      glEnd();
   }
}

void glDrawRiggingEdges(Mesh *model, USkeleton *skeleton, bool usingbones) {
   
   if (model == 0) return;
   if (skeleton == 0) return;
   
   glPushAttrib(GL_COLOR_BUFFER_BIT | GL_POINT_BIT | GL_ENABLE_BIT | GL_POLYGON_BIT);
   glDisable(GL_LIGHTING);
   glEnable(GL_POINT_SMOOTH);
   glEnable(GL_BLEND);
   glEnable(GL_CULL_FACE);
   
   glColor4f(1.0, 0.5, 0.0, 1.0);
   glLineWidth(0.5);
   glBegin(GL_LINES);
   Vertex_iterator vi = model->vertices_begin();
   while (vi != model->vertices_end()) {
      const Point3 &pv = vi->point();
      const int &j = vi->rigging_group;
      assert(j != -1);
      if (usingbones) {
         if (j==-1) {
            //~ glVertex3f(pv[0], pv[1], pv[2]);
            //~ glVertex3f(0, 0, 0);
         }
         else {
            const Segment3 &b = skeleton->boneSegment(j);
            const Point3 &p = b.source();
            const Point3 &q = b.target();
            glVertex3f(pv[0], pv[1], pv[2]);
            glVertex3f((p[0]+q[0])*0.5, (p[1]+q[1])*0.5, (p[2]+q[2])*0.5);
         }
      }
      else {
         if (j == -1) { ++vi; continue; }
         assert(j != -1 and "rigging group -1");
         assert(j < skeleton->nJoints() and " j > nJoints ");
         if (j >= skeleton->nJoints()) cout<<"j :::: "<<j<<endl;
         const Point3 &p = skeleton->getJointPos(j);
         glVertex3f(pv[0], pv[1], pv[2]);
         glVertex3f(p[0], p[1], p[2]);
      }
      ++vi;
   }
   glEnd();
   glPopAttrib();
   
   
   //~ glColor4f(0.0, 1.0, 0.0, 1.0);
   //~ vi = model->vertices_begin();
   //~ Vertex_iterator vini = model->vertices_begin();
   //~ while (vi != model->vertices_end()) {
      //~ const int &i = vi->_vertex_parent;
      //~ if (i!=-1) cout<<" i: "<<i<<endl;
      //~ if (i != -1) {
         //~ const Point3 &p = vi->point();
         //~ const Point3 &q = vini[i].point();
         //~ glBegin(GL_POINTS);
         //~ glVertex3f(p[0], p[1], p[2]);
         //~ glVertex3f(q[0], q[1], q[2]);
         //~ glEnd();
      //~ }
      //~ ++vi;
   //~ }
}

void generateColors() {
   
   colores.reserve(300);
   list<Point3> cores;
   float step=0.2; bool flag = true;
   for (float r=0.0; r<=1.0; r+=step) 
      for (float g=1.0; g>=0.0; g-=step) 
         for (float b=0.0; b<=1.0; b+=step) {
            //~ if (flag) {
               //~ cores.push_back(Point3(r,g,b));
               //~ cores.push_back(Point3(b,r,g));
               //~ cores.push_back(Point3(g,b,r));
            //~ }
            //~ else {
               //~ cores.push_front(Point3(r,g,b));
               //~ cores.push_front(Point3(b,r,g));
               //~ cores.push_front(Point3(g,b,r));
            //~ }
            
            if (flag) cores.push_back(Point3(r,g,b));
            else      cores.push_front(Point3(b,r,g));
            flag = not flag;
         }
   
   colores.push_back(Point3(0.0,0.5,1.0));
   
   colores.push_back(Point3(0.75,0.75,0.0));
   colores.push_back(Point3(0.75,0.0,0.75));
   colores.push_back(Point3(0.5,0.0,0.0));
   colores.push_back(Point3(0.0,1.0,0.5));
   colores.push_back(Point3(0.0,0.0,0.25));
   
   colores.push_back(Point3(0.0,0.3,0.0));
   colores.push_back(Point3(0.7,0.2,0.2));
   colores.push_back(Point3(0.1,0.3,0.3));
   colores.push_back(Point3(0.0,0.2,0.4));
   colores.push_back(Point3(0.5,0.0,1.0));
   
   colores.push_back(Point3(0.4,0.2,0.3));
   colores.push_back(Point3(0.7,0.0,0.0));
   colores.push_back(Point3(0.5,0.5,0.2));
   colores.push_back(Point3(0.25,0.5,0.0));
   colores.push_back(Point3(1.0,0.0,0.5));
   
   colores.push_back(Point3(0.25,0.0,0.0));
   colores.push_back(Point3(1.0,0.5,0.0));
   colores.push_back(Point3(1.0,0.0,0.0));
   colores.push_back(Point3(0.0,1.0,0.0));
   colores.push_back(Point3(0.5,0.0,0.0));
   
   colores.push_back(Point3(0.3,0.3,0.1));
   colores.push_back(Point3(0.6,0.6,0.4));
   
   colores.insert(colores.end(), cores.begin(), cores.end());
   
   
   /* 
   // versao 
   colores.reserve(300);
   list<Point3> cores;
   float step=0.2; bool flag = true;
   for (float r=0.0; r<=1.0; r+=step) 
      for (float g=1.0; g>=0.0; g-=step) 
         for (float b=0.0; b<=1.0; b+=step) {
            //~ if (flag) {
               //~ cores.push_back(Point3(r,g,b));
               //~ cores.push_back(Point3(b,r,g));
               //~ cores.push_back(Point3(g,b,r));
            //~ }
            //~ else {
               //~ cores.push_front(Point3(r,g,b));
               //~ cores.push_front(Point3(b,r,g));
               //~ cores.push_front(Point3(g,b,r));
            //~ }
            
            if (flag) cores.push_back(Point3(r,g,b));
            else      cores.push_front(Point3(b,r,g));
            flag = not flag;
         }
   
   colores.push_back(Point3(0.25,0.0,0.0));
   
   colores.push_back(Point3(0.75,0.75,0.0));
   colores.push_back(Point3(0.75,0.0,0.75));
   colores.push_back(Point3(0.5,0.0,0.0));
   colores.push_back(Point3(0.0,1.0,0.5));
   colores.push_back(Point3(0.0,0.0,0.25));
   
   colores.push_back(Point3(0.0,0.3,0.0));
   colores.push_back(Point3(0.7,0.2,0.2));
   colores.push_back(Point3(0.1,0.3,0.3));
   colores.push_back(Point3(0.0,0.2,0.4));
   colores.push_back(Point3(0.5,0.0,1.0));
   
   colores.push_back(Point3(0.4,0.2,0.3));
   colores.push_back(Point3(0.7,0.0,0.0));
   colores.push_back(Point3(0.5,0.5,0.2));
   colores.push_back(Point3(0.25,0.5,0.0));
   colores.push_back(Point3(1.0,0.0,0.5));
   
   colores.push_back(Point3(0.0,0.5,1.0));
   colores.push_back(Point3(1.0,0.5,0.0));
   colores.push_back(Point3(1.0,0.0,0.0));
   colores.push_back(Point3(0.0,1.0,0.0));
   colores.push_back(Point3(0.5,0.0,0.0));
   
   colores.push_back(Point3(0.0,0.5,0.0));
   
   colores.insert(colores.end(), cores.begin(), cores.end());
   
   //~ colores[14] = Point3(0.4,0.4,0.4);
   //~ colores[21] = Point3(0.3,0.0,1.0);
   //~ colores[23] = Point3(0.0,0.0,1.0);
   //~ colores[24] = Point3(1.0,0.0,0.0);
   */
}
